Skip to content

[EXPERMENT] Refactor the GUI into a modular package with dynamic command forms and progress hooks#83

Draft
RenanGBarreto wants to merge 12 commits into
mainfrom
feat/gui-update
Draft

[EXPERMENT] Refactor the GUI into a modular package with dynamic command forms and progress hooks#83
RenanGBarreto wants to merge 12 commits into
mainfrom
feat/gui-update

Conversation

@RenanGBarreto

@RenanGBarreto RenanGBarreto commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Refactor the GUI into a modular package with dynamic command forms and progress hooks

🤔 Why?

  • The old gui.py was a single 1,300-line monolith — hard to navigate, extend, or share logic with.
  • The GUI hard-coded the pack folder form fields and had no way to discover new commands automatically.
  • The CLI and GUI had no shared metadata, so the GUI could not know about new options or subcommands without manual edits.
  • Progress from CLI commands was invisible in the GUI — no way to show a packing or verification progress bar.

🔧 What changed

  • Split gui.py into a mkpfs/gui/ package with focused modules: app, widgets, panels, theme, i18n, __main__.
  • Added mkpfs/discovery.py — a lightweight, import-safe module that exposes CLI metadata (commands, options) and a global progress-event registry. The GUI uses this to build form fields dynamically instead of hard-coding them.
  • Added a shared mkpfs.__init__ API that re-exports discovery helpers so GUI and CLI consumers use the same functions for output naming, path normalization, and progress events.
  • The GUI now discovers all subcommands (pack folder, pack file, pack exfat, verify, inspect, tree, unpack) and their arguments by introspecting the live argparse parsers — no more hard-coded form layouts.
  • Progress from Progress and pfs operations is emitted as typed ProgressEvents and forwarded to any registered handler, so the GUI can show live progress.
  • pbar.py and pfs.py now emit progress events through the global registry.
  • The window is responsive, resizable, and maximizable — the layout adapts properly instead of being fixed-size.
  • Added default_output_name() for consistent, argparse-friendly default filenames across CLI and GUI.

🧪 How to test

  • Run python -m mkpfs gui — the window should show all commands with dynamically built forms.
  • Each command's form fields should match its --help arguments.
  • Run a pack folder operation from the GUI — progress should show in the log/progress area.
  • Resize the window — layout should adapt responsively.

💬 Notes for non-technical readers

  • The GUI looks and works the same as before, but its internals are now organized into separate files so improvements are easier to make.
  • Forms for every command are now built automatically — no manual coding needed when we add new commands.
  • Progress from pack, verify, and other operations now shows up in the GUI.
  • The window can be resized and maximized properly.
  • All existing CLI commands work exactly as before — nothing changed there.

…s | decisions (explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events; discover commands/options dynamically by first importing the mkpfs package and fall back to parsing mkpfs --help if import metadata isn't available"
…cit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events; discover commands/options dynamically by first importing the mkpfs package and fall back to parsing mkpfs --help if import metadata isn't available (TASK_0001)
…explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): keep CustomTkinter/tkinter to minimize scope, preserve current behavior and packaging, and focus on modularization (TASK_0002)
… forms | decisions (explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): discover commands/options dynamically by first importing the mkpfs package and fall back to parsing mkpfs --help if import metadata isn't available; allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events; keep CustomTkinter/tkinter to minimize scope"
…explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): discover commands/options dynamically by first importing the mkpfs package and fall back to parsing mkpfs --help if import metadata isn't available; allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events; keep CustomTkinter/tkinter to minimize scope (TASK_0003)
…sioning, and progress mirroring | decisions (explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events"
…sioning, and progress mirroring | decisions (explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events"
…s mirroring | decisions (explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): allow a small shared API in mkpfs itself for command metadata, default output naming, and progress events (TASK_0004)
…ons (explicit user choices — these OVERRIDE the spec doc wherever they conflict; follow them exactly): keep CustomTkinter/tkinter to minimize scope, preserve current behavior and packaging (TASK_0005)
Copilot AI review requested due to automatic review settings July 5, 2026 16:26
@github-code-quality

github-code-quality Bot commented Jul 5, 2026

Copy link
Copy Markdown

Code Coverage Overview

Languages: Python

Python / code-coverage/pytest

The overall coverage in the branch is 65%. The coverage in the branch is 75%.

Show a code coverage summary of the most impacted files.
File 3c9064e 5e2e545 +/-
pfs.py 78% 78% 0%
__init__.py 100% 100% 0%
gui/__init__.py 0% 0% 0%
gui/__main__.py 0% 0% 0%
gui/app.py 0% 0% 0%
gui/i18n.py 0% 0% 0%
gui/panels.py 0% 0% 0%
gui/theme.py 0% 0% 0%
gui/widgets.py 0% 0% 0%
discovery.py 0% 38% +38%

Updated July 05, 2026 17:49 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

Comment thread mkpfs/gui.py
result = _convert_discovery_meta(meta)
if result:
return result
except Exception:
Comment thread mkpfs/gui.py
result = _ensure_argparse_meta()
if result:
return result
except Exception:
Comment thread mkpfs/gui.py
meta = get_cli_metadata(prefer_import=False)
if meta.get("commands"):
result = _convert_discovery_meta(meta)
except Exception:
Comment thread mkpfs/pfs.py
bytes_processed=len(block),
timestamp=time.time(),
)
except Exception:
Comment thread mkpfs/pfs.py
bytes_processed=len(chunk),
timestamp=time.time(),
)
except Exception:
Comment thread mkpfs/pfs.py
bytes_processed=raw_block_len,
timestamp=time.time(),
)
except Exception:
Comment thread mkpfs/pfs.py
bytes_processed=raw_len,
timestamp=time.time(),
)
except Exception:
Comment thread mkpfs/pfs.py
bytes_processed=len(chunk),
timestamp=time.time(),
)
except Exception:
Comment thread mkpfs/pfs.py
bytes_processed=len(raw_chunk),
timestamp=time.time(),
)
except Exception:
Comment thread mkpfs/gui.py

# Key for 'pack folder' and 'pack file' subcommands. The root parser uses
# 'command' and 'pack_command' to mirror argparse's nested subparser dests.
_PACK_SUBKEY: dict[str, str] = {

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the GUI to better track the evolving CLI surface by adding runtime command/option discovery, default output naming helpers, and a progress-event plumbing layer that can be mirrored into the GUI. It also introduces a mkpfs.gui package compatibility layer while keeping the original mkpfs/gui.py shim.

Changes:

  • Add mkpfs.discovery for CLI discovery (import/help fallback), default output naming, and a global progress-event registry.
  • Wire progress-event emission from CLI progress (mkpfs.pbar.Progress) and PFSC compression hotspots (mkpfs.pfs) to support GUI progress mirroring.
  • Expand GUI implementation in mkpfs/gui.py to support dynamic navigation/panels based on discovered argparse metadata, plus add mkpfs/gui/ package entrypoints and re-export modules.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
mkpfs/pfs.py Emits global progress events during PFSC compression when a progress callback is present.
mkpfs/pbar.py Emits global progress events from TUI progress updates and status messages.
mkpfs/gui/widgets.py New re-export façade for widget classes from the shim module.
mkpfs/gui/theme.py New re-export façade for theme constants from the shim module.
mkpfs/gui/panels.py New re-export façade for panel classes from the shim module.
mkpfs/gui/i18n.py New re-export façade for translations/helpers from the shim module.
mkpfs/gui/app.py New re-export façade for app entry points from the shim module.
mkpfs/gui/__main__.py Adds python -m mkpfs.gui entrypoint with shim-loading fallback.
mkpfs/gui/__init__.py Adds package initializer that loads the shim and copies exports for compatibility.
mkpfs/gui.py Major GUI updates: argparse metadata introspection, dynamic panels, output prefill, footer version, and progress mirroring hooks.
mkpfs/discovery.py New lightweight module providing CLI discovery, output naming helpers, and global progress registry.
mkpfs/__init__.py Re-exports discovery/progress APIs at package root.
.pi-tasks/TASK_AUTO_0001.md Task tracking documentation for the GUI refresh effort.
.pi-tasks/TASK_0005.md Task tracking documentation for window responsiveness requirements.
.pi-tasks/TASK_0004.md Task tracking documentation for output naming/footer/progress mirroring.
.pi-tasks/TASK_0003.md Task tracking documentation for dynamic command navigation/forms.
.pi-tasks/TASK_0002.md Task tracking documentation for GUI package split.
.pi-tasks/TASK_0001.md Task tracking documentation for shared discovery/progress hooks.
.pi-tasks/.ignore Excludes .pi-tasks/ from fd/ripgrep discovery while keeping them tracked by git.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread mkpfs/gui.py
Comment on lines +2908 to +2910
self._pages = list[tuple[str, str, type | None, str]](dynamic_pages)
self._dynamic = True
self._dynamic_meta: dict[str, list[ArgOption]] = meta
Comment thread mkpfs/gui.py
Comment on lines +1007 to +1017
# Tier 1: use the discovery module's get_cli_metadata (import or help fallback).
try:
from mkpfs.discovery import get_cli_metadata

meta = get_cli_metadata(prefer_import=True)
if meta.get("commands"):
result = _convert_discovery_meta(meta)
if result:
return result
except Exception:
pass
Comment thread mkpfs/gui.py
Comment on lines +1331 to +1337
def _on_progress(event: Any) -> None:
"""Mirror a ProgressEvent into this panel's progress bar."""
with contextlib.suppress(Exception):
pct: float = event.done / max(event.total, 1)
self._progress.set(pct)
self.after(0, lambda: self._progress.configure(mode="determinate"))

Comment thread mkpfs/gui.py
Comment on lines +1355 to +1358
# Reset progress bar to zero determinate before each new command.
self._progress.stop()
self._progress.configure(mode="determinate")
self._progress.set(0)
Comment thread mkpfs/discovery.py
Comment on lines +205 to +213
# First try import-time metadata when requested and available.
if prefer_import:
try:
from . import cli_metadata as meta
except Exception:
meta = None
if meta:
return {"source": "import", "commands": meta}

Comment thread mkpfs/gui/__init__.py
Comment on lines +1 to +12
"""mkpfs.gui package — compatibility layer that defers to the top-level shim.

We must preserve the original import semantics where importing ``mkpfs.gui``
returns the shim module located at ``mkpfs/gui.py`` (so that callers relying
on Path(__file__) for asset resolution keep working). To achieve that while
also providing a directory containing the refactored source files for
packaging/tooling, this package initializer loads the shim from the module
file and then replaces the package entry in sys.modules with that module
object. The shim module is given a __path__ pointing at this directory so
``mkpfs.gui.<submodule>`` imports (e.g. ``mkpfs.gui.__main__``) continue to
work and submodules can be imported from the package directory.
"""
@RenanGBarreto RenanGBarreto marked this pull request as draft July 5, 2026 17:54
@RenanGBarreto RenanGBarreto changed the title Feat/gui update Refactor the GUI into a modular package with dynamic command forms and progress hooks Jul 5, 2026
@RenanGBarreto RenanGBarreto changed the title Refactor the GUI into a modular package with dynamic command forms and progress hooks [EXPERMENT] Refactor the GUI into a modular package with dynamic command forms and progress hooks Jul 5, 2026
@RenanGBarreto RenanGBarreto added do not merge Do no merge the changes investigation needed We would need to investigate. and removed feature labels Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do not merge Do no merge the changes investigation needed We would need to investigate.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants